#define _GNU_SOURCE

#include "tssp_internal.h"

#include <time.h>
#include "event.h"
#include "tssp.h"
#include "tssp_pat.h"
#include "tssp_pmt.h"
#include "tssp_dvb_sdt.h"
#include "tssp_dvb_eit.h"
#include "tssp_dvb_ait.h"
#include "tssp_timer_impl.h"
#include "tssp_dvb_dr_00.h"
#include "tssp_dvb_dr_01.h"
#include "tssp_dvb_dr_02.h"
#include "tssp_dvb_dr_13.h"
#include "tssp_dvb_dr_56.h"
#include "tssp_dvb_dr_66.h"
#include "tssp_dvb_dr_6F.h"

#define BATCH_SIZE  100

struct pmt_monitor {
    struct context *context;
    struct tssp_pmt_monitor *mon;
    STAILQ_ENTRY(pmt_monitor) entry;
};

struct ait_monitor {
    struct context *context;
    struct tssp_dvb_ait_monitor *mon;
    STAILQ_ENTRY(ait_monitor) entry;
};

struct eit_monitor {
    struct context *context;
    struct tssp_dvb_eit_monitor *mon;
    STAILQ_ENTRY(eit_monitor) entry;
};

struct context {
    int n;
    struct tssp *parser;
    struct event ev;
    struct timeval tv;
    struct tssp_section_listener *pat_listener;
    struct tssp_section_listener *ait_listener;
    struct tssp_pat_monitor *pat_monitor;
    struct tssp_dvb_sdt_monitor *sdt_monitor;
    STAILQ_HEAD(pmt_monitors, pmt_monitor) pmt_monitors;
    STAILQ_HEAD(ait_monitors, ait_monitor) ait_monitors;
    STAILQ_HEAD(eit_monitors, eit_monitor) eit_monitors;
};

static
void packet_timer_cb(int fd, short reason, void *userdata)
{
    int i;
    uint8_t packet[188];
    struct context *context = userdata;

    evtimer_add(&context->ev, &context->tv);

    for (i = 0; i < BATCH_SIZE; i++) {
        /* read a packet */
        if (fread(packet, 188, 1, stdin) != 1) {
            event_del(&context->ev);
            event_loopexit(NULL);
            break;
        }

        /* check sync byte */
        if (packet[0] == 0x47) {
            tssp_push_packet(context->parser, packet);
#if 0
            if (context->n && context->n % 10000 == 0)
                printf("sent %d packets\n", context->n);
#endif
        } else {
            printf("invalid sync byte\n");
        }

        context->n++;
    }
}

static
void print_indent(int depth)
{
    int i;
    for (i = 0; i < depth; i++)
        printf("    ");
}

static
void dump_application_descriptor(struct tssp_descriptor *desc, int depth, void *param)
{
    int i;
    int ret;
    struct tssp_dr_application app;

    if (desc == NULL)
        return;

    ret = tssp_dr_application_parse(&app, desc);
    if (ret != TSSP_STATUS_OK)
        return;

    /* profiles */
    for (i = 0; i < app.profile_count; i++) {
        print_indent(depth);
        printf("profile(0x%04x [%d.%d.%d])\n",
               app.profile[i].profile,
               app.profile[i].version.major,
               app.profile[i].version.minor,
               app.profile[i].version.micro);
    }

    /* transport_protocol_label */
    print_indent(depth);
    printf("service_bound_flag(%d), visibility(%x), labels(%d)\n",
           app.service_bound_flag ? 1 : 0,
           app.visibility,
           app.transport_protocol_length);

    print_indent(depth);
    printf("protocol_label([");
    for (i = 0; i < app.transport_protocol_length; i++) {
        printf(i ? ", 0x%02x" : "0x%02x",
               app.transport_protocol_label[i]);
    }
    printf("])\n");
}

static
void dump_application_name_descriptor(struct tssp_descriptor *desc, int depth, void *param)
{
    int i;
    int ret;
    struct tssp_dr_application_name app_name;
    uint8_t *value;

    if (desc == NULL)
        return;

    ret = tssp_dr_application_name_parse(&app_name, desc);
    if (ret != TSSP_STATUS_OK)
        return;

    for (i = 0; i < app_name.count; i++) {
        value = app_name.lang[i].name;

        print_indent(depth);

        /* skip character table, see Annex A of A038 */
        if (value[0] < 0x20) {
            printf("char_table(0x%02x), ", value[0]);
            value++;
        }

        printf("lang_code(0x%06x), value(%s)\n",
               app_name.lang[i].code, value);
    }
}

static
void dump_transport_protocol_descriptor(struct tssp_descriptor *desc, int depth, void *param)
{
    int i;
    int j;
    int ret;
    struct tssp_dr_transport_protocol protocol;

    ret = tssp_dr_transport_protocol_parse(&protocol, desc);
    if (ret != TSSP_STATUS_OK)
        return;

    print_indent(depth);
    printf("protocol_id(0x%04x), label(0x%02x)\n",
           protocol.protocol_id, protocol.label);

    if (protocol.protocol_id == 0x0001) {
        print_indent(depth);
        if (protocol.selector_byte.oc.remote_connection) {
            printf("original_network_id(0x%04x), ts_id(0x%04x), service_id(0x%04x), ",
                   protocol.selector_byte.oc.original_network_id,
                   protocol.selector_byte.oc.transport_stream_id,
                   protocol.selector_byte.oc.service_id);
        }
        printf("component_tag(0x%02x)\n", protocol.selector_byte.oc.component_tag);
    } else if (protocol.protocol_id == 0x0003) {
        for (i = 0; i < protocol.selector_byte.ic.count; i++) {
            struct tssp_dr_IC_transport *ic = protocol.selector_byte.ic.ic;
            print_indent(depth);
            printf("URL_base(%s)", ic[i].URL_base);
            for (j = 0; j < ic[i].URL_extension_count; j++)
                printf(", URL_extension(%s)", ic[i].URL_extension[j]);
            printf("\n");
        }
    }
}

static
void dump_carousel_identifier_descriptor(struct tssp_descriptor *desc, int depth, void *param)
{
    int ret;
    struct tssp_dr_carousel_identifier dest;

    ret = tssp_dr_carousel_identifier_parse(&dest, desc);
    if (ret != TSSP_STATUS_OK)
        return;

    print_indent(depth);
    printf("carousel_id(0x%08x), FormatID(0x%02x)\n",
           dest.carousel_id, dest.FormatID);

    if (dest.FormatID == 0x00) {
        print_indent(depth);
        printf("private_data_length(%d)\n", dest.u.standard.private_data_length);
    } else if (dest.FormatID == 0x01) {
        print_indent(depth);
        printf("ModuleVersion(%d), ModuleId(0x%04x), BlockSize(%d), ModuleSize(%d)\n",
               dest.u.enhanced.ModuleVersion, dest.u.enhanced.ModuleId,
               dest.u.enhanced.BlockSize, dest.u.enhanced.ModuleSize);
        print_indent(depth);
        printf("CompressionMethod(0x%02x), OriginalSize(%d), TimeOut(%d)\n",
               dest.u.enhanced.CompressionMethod,
               dest.u.enhanced.OriginalSize, dest.u.enhanced.TimeOut);
        print_indent(depth);
        printf("ObjectKeyLength(%d), private_data_length(%d)\n",
               dest.u.enhanced.ObjectKeyLength, dest.u.enhanced.private_data_length);
    }
}

static
void dump_string_descriptor(struct tssp_descriptor *desc, int depth, void *param)
{
    const char *field_value = param;
    char *value;
    value = strndup((char *)desc->data, desc->length);
    if (value) {
        print_indent(depth);
        printf("%s(%s)\n", field_value, value);
        free(value);
    }
}

static
void dump_application_usage_descriptor(struct tssp_descriptor *desc, int depth, void *param)
{
    if (desc->length == 1) {
        print_indent(depth);
        printf("usage_type(0x%02x)\n", *desc->data);
    }
}

static
void dump_stream_identifier_descriptor(struct tssp_descriptor *desc, int depth, void *param)
{
    if (desc->length == 1) {
        print_indent(depth);
        printf("component_tag(0x%02x)\n", *desc->data);
    }
}

static
void dump_teletext_descriptor(struct tssp_descriptor *desc, int depth, void *param)
{
    int i;
    int ret;
    struct tssp_dr_teletext dest;

    ret = tssp_dr_teletext_parse(&dest, desc);
    if (ret != TSSP_STATUS_OK)
        return;

    for (i = 0; i < dest.count; i++) {
        print_indent(depth);
        printf("ISO_639_language_code(0x%08x), type(0x%02x), magazine_number(0x%02x), page_number(0x%02x)\n",
               dest.data[i].ISO_639_language_code,
               dest.data[i].type,
               dest.data[i].magazine_number,
               dest.data[i].page_number);
    }
}

static
void dump_data_broadcast_id_descriptor(struct tssp_descriptor *desc, int depth, void *param)
{
    int i;
    int ret;
    struct tssp_dr_data_broadcast_id dest;

    ret = tssp_dr_data_broadcast_id_parse(&dest, desc);
    if (ret != TSSP_STATUS_OK)
        return;

    print_indent(depth);
    printf("id(0x%04X)", dest.id);
    if (dest.id == 0x00F0 || dest.id == 0x00F1) {
        printf(", application_type([");
        for (i = 0; i < dest.data.application.count; i++) {
            printf(i ? ", 0x%02x" : "0x%04x", dest.data.application.type[i]);
        }
        printf("])");
    }
    printf("\n");
}

static
void dump_application_signalling_descriptor(struct tssp_descriptor *desc, int depth, void *param)
{
    int i;
    int ret;
    struct tssp_dr_application_signalling dest;

    ret = tssp_dr_application_signalling_parse(&dest, desc);
    if (ret != TSSP_STATUS_OK)
        return;

    for (i = 0; i < dest.count; i++) {
        print_indent(depth);
        printf("application_type(0x%04x), AIT_version_number(%d)\n",
               dest.data[i].application_type,
               dest.data[i].AIT_version_number);
    }
}

const struct desc_entry {
    uint16_t tag;
    const char *name;
    void (*func)(struct tssp_descriptor *desc, int depth, void *param);
    void *param;
} desc_table[] = {
    {0x00, "application_descriptor", dump_application_descriptor, NULL},
    {0x01, "application_name_descriptor", dump_application_name_descriptor, NULL},
    {0x02, "transport_protocol_descriptor", dump_transport_protocol_descriptor, NULL},
    {0x13, "carousel_identifier_descriptor", dump_carousel_identifier_descriptor, NULL},
    {0x15, "simple_application_location_descriptor", dump_string_descriptor, "initial_path_bytes"},
    {0x16, "application_usage_descriptor", dump_application_usage_descriptor, NULL},
    {0x52, "stream_identifier_descriptor", dump_stream_identifier_descriptor, NULL},
    {0x56, "teletext_descriptor", dump_teletext_descriptor, NULL},
    {0x66, "data_broadcast_id_descriptor", dump_data_broadcast_id_descriptor, NULL},
    {0x6F, "application_signalling_descriptor", dump_application_signalling_descriptor, NULL},
    {0x71, "service_identifier_descriptor", dump_string_descriptor, "textual_service_identifier_bytes"},
};

static
void dump_descriptor(struct tssp_descriptor *desc, int depth)
{
    int i;
    const struct desc_entry *entry = NULL;

    if (desc == NULL)
        return;


    for (i = 0; i < (sizeof(desc_table)/sizeof(*desc_table)); i++) {
        if (desc->tag == desc_table[i].tag) {
            entry = &desc_table[i];
            break;
        }
    }

    print_indent(depth);
    if (entry) {
        printf("%s: tag(0x%02x), length(%d)\n", entry->name, desc->tag, desc->length);
        entry->func(desc, depth + 1, entry->param);
    } else {
        printf("tag(0x%02x), length(%d)\n", desc->tag, desc->length);
    }
}


static
void dump_descriptors(struct tssp_descriptors *descriptors, int depth)
{
    struct tssp_descriptor *desc;

    STAILQ_FOREACH(desc, descriptors, entry)
        dump_descriptor(desc, depth);
}

#if 0
static
void pmt_callback(int status,
                             struct tssp_section_listener *listener,
                             struct tssp_section *section,
                             void *userdata)
{
    struct tssp_pmt *pmt;
    struct tssp_pmt_es *es;

#if 0
    printf("#### %s() called\n", __FUNCTION__);
#endif

    pmt = tssp_pmt_parse(section);
    tssp_section_destroy(section);

    if (pmt == NULL)
        return;

    printf("PMT: program_number(0x%04x), version(%d), current_next(%d)\n",
           pmt->program_number, pmt->version, pmt->current_next);

    STAILQ_FOREACH(es, &pmt->es_list, entry)
        printf("    type(0x%02x), pid(0x%04x)\n", es->type, es->pid);

    tssp_pmt_destroy(pmt);
}

static
void pat_callback(int status,
                  struct tssp_section_listener *listener,
                  struct tssp_section *section,
                  void *userdata)
{
    struct tssp_pat *pat;
    struct tssp_pat_program *pat_program;
    struct context *context = userdata;
    struct tssp_section_filter filter = {
        .pid = TSSP_UNKNOWN_PID,
        .mask = {0xFF, 0},
        .value = {0x02, 0},
        .timeout = 0,
    };

#if 0
    printf("#### %s() called\n", __FUNCTION__);
#endif

#if 0
    printf("section_number(%d), last_section_number(%d)\n",
           section->number, section->last_number);
#endif

    pat = tssp_pat_parse(section);
    tssp_section_destroy(section);

    if (pat == NULL)
        return;

    printf("PAT: ts_id(0x%04x), version(%d), current_next(%d)\n",
           pat->ts_id, pat->version, pat->current_next);

    STAILQ_FOREACH(pat_program, &pat->programs, entry) {
        if (pat_program->number == 0) {
            printf("    program_number(0x%04x), network_PID(0x%04x)\n",
                   pat_program->number, pat_program->pid);
        } else {
            printf("    program_number(0x%04x), program_map_PID(0x%04x)\n",
                   pat_program->number, pat_program->pid);
            filter.pid = pat_program->pid;
            tssp_add_section_filter(context->parser,
                                    &filter,
                                    pmt_callback,
                                    context);

        }
    }

    tssp_pat_destroy(pat);
}
#endif

#if 0
static
void sdt_callback(int status,
                  struct tssp_section_listener *listener,
                  struct tssp_section *section,
                  void *userdata)
{
    printf("### %s() called\n", __FUNCTION__);

    printf("table_id = 0x%02x\n", section->table_id);
    printf("syntax_indicator = %d\n", section->syntax_indicator);
    printf("private_indicator = %d\n", section->private_indicator);
    printf("length = %d\n", section->length);
    printf("extension = 0x%04x\n", section->extension);
    printf("version = %d\n", section->version);
    printf("number = %d\n", section->number);
    printf("last_number = %d\n", section->last_number);
}
#endif

static
void remove_ait_monitor(struct context *context, struct ait_monitor *mon)
{
    tssp_dvb_ait_monitor_detach(mon->mon);
    STAILQ_REMOVE(&context->ait_monitors, mon, ait_monitor, entry);
    free(mon);
}

static
void ait_monitor_callback(struct tssp_dvb_ait_monitor *mon,
                          struct tssp_dvb_ait *ait,
                          struct tssp_section *section,
                          void *userdata)
{
    struct ait_monitor *ait_mon = userdata;
    struct tssp_dvb_ait_app *app;

    printf("### %s() called\n", __FUNCTION__);
    printf("    test_flag(%d), application_type(0x%04x)\n",
           ait->test_flag, ait->application_type);

    dump_descriptors(&ait->descriptors, 1);

    STAILQ_FOREACH(app, &ait->apps, entry) {
        printf("    org_id(0x%08x), app_id(0x%04x), control_code(0x%02x)\n",
               app->org_id, app->app_id, app->control_code);
        dump_descriptors(&app->descriptors, 2);
    }

    tssp_dvb_ait_destroy(ait);
    tssp_section_destroy(section);
    remove_ait_monitor(ait_mon->context, ait_mon);
}

static
struct ait_monitor *
add_ait_monitor(struct context *context, uint16_t pid, uint16_t app_type)
{
    struct ait_monitor *ait_mon;

    ait_mon = malloc(sizeof(*ait_mon));
    if (ait_mon == NULL)
        return NULL;

    ait_mon->context = context;
    ait_mon->mon = tssp_dvb_ait_monitor_attach(context->parser,
                                           pid,
                                           app_type,
                                           ait_monitor_callback,
                                           ait_mon);
    if (ait_mon->mon) {
        STAILQ_INSERT_TAIL(&context->ait_monitors, ait_mon, entry);
    } else {
        free(ait_mon);
        ait_mon = NULL;
    }

    return ait_mon;
}

static
void remove_pmt_monitor(struct context *context, struct pmt_monitor *mon)
{
    tssp_pmt_monitor_detach(mon->mon);
    STAILQ_REMOVE(&context->pmt_monitors, mon, pmt_monitor, entry);
    free(mon);
}

static
void pmt_monitor_callback(struct tssp_pmt_monitor *mon,
                          struct tssp_pmt *pmt,
                          struct tssp_section *section,
                          void *userdata)
{
    int found = 0;
    int i;
    struct tssp_descriptor *desc;
    struct tssp_dr_application_signalling sig;
    struct tssp_pmt_es *es;
    struct pmt_monitor *pmt_mon = userdata;

    printf("### %s() called\n", __FUNCTION__);
    printf("    service_id(0x%04x)\n", pmt->program_number);

    dump_descriptors(&pmt->descriptors, 1);

    STAILQ_FOREACH(es, &pmt->es_list, entry) {
        printf("    ES: type(0x%02x), PID(0x%04x)\n", es->type, es->pid);

        dump_descriptors(&es->descriptors, 2);

        if (es->type == 0x05) {
            STAILQ_FOREACH(desc, &es->descriptors, entry) {
                if (desc->tag != 0x6F)
                    continue;

                i = tssp_dr_application_signalling_parse(&sig, desc);
                if (i != TSSP_STATUS_OK)
                    continue;

                for (i = 0; i < sig.count; i++) {
                    add_ait_monitor(pmt_mon->context, es->pid,
                                    sig.data[i].application_type);
                }

                found = 1;
                break;
            }

            if (found == 0)
                add_ait_monitor(pmt_mon->context, es->pid, 0x0010);
        }
    }
    printf("    PCR_PID(0x%04x)\n", pmt->pcr_pid);

    tssp_pmt_destroy(pmt);
    tssp_section_destroy(section);
    remove_pmt_monitor(pmt_mon->context, pmt_mon);
}

static
struct pmt_monitor *
add_pmt_monitor(struct context *context, uint16_t service_id, uint16_t pid)
{
    struct pmt_monitor *pmt_mon;

    pmt_mon = malloc(sizeof(*pmt_mon));
    if (pmt_mon == NULL)
        return NULL;

    pmt_mon->context = context;
    pmt_mon->mon = tssp_pmt_monitor_attach(context->parser,
                                           service_id,
                                           pid,
                                           1,
                                           pmt_monitor_callback,
                                           pmt_mon);
    if (pmt_mon->mon) {
        STAILQ_INSERT_TAIL(&context->pmt_monitors, pmt_mon, entry);
    } else {
        free(pmt_mon);
        pmt_mon = NULL;
    }

    return pmt_mon;
}

static
void remove_eit_monitor(struct context *context, struct eit_monitor *mon)
{
    tssp_dvb_eit_monitor_detach(mon->mon);
    STAILQ_REMOVE(&context->eit_monitors, mon, eit_monitor, entry);
    free(mon);
}

static
void eit_monitor_callback(struct tssp_dvb_eit_monitor *monitor,
                          struct tssp_dvb_eit_list *eit_list,
                          struct tssp_section *section,
                          void *userdata)
{
    struct eit_monitor *eit_mon = userdata;
    struct tssp_dvb_eit *eit;
    struct tssp_dvb_eit_event *eit_event;

    printf("### %s() called\n", __FUNCTION__);

    STAILQ_FOREACH(eit, eit_list, entry) {
        printf("    service_id(0x%04x), version(%d), current_next(%d)\n",
               eit->service_id, eit->version, eit->current_next);
        printf("    ts_id(0x%04x), network_id(0x%04x), segment_last_number(%d), last_table_id(0x%02x)\n",
               eit->ts_id, eit->network_id, eit->segment_last_section_number, eit->last_table_id);
 
        STAILQ_FOREACH(eit_event, &eit->events, entry) {
            printf("    event_id(0x%04x), start_time(%llu), duration(%u), running_status(%d), free_CA_mode(%d)\n",
                   eit_event->event_id,
                   eit_event->start_time,
                   eit_event->duration,
                   eit_event->running_status,
                   eit_event->free_CA_mode);
            dump_descriptors(&eit_event->descriptors, 2);
        }
    }

    tssp_dvb_eit_list_destroy(eit_list);
    tssp_section_destroy(section);
    remove_eit_monitor(eit_mon->context, eit_mon);
}

static
struct eit_monitor *
add_eit_monitor(struct context *context, uint16_t service_id, uint8_t table_id)
{
    struct eit_monitor *eit_mon;

    eit_mon = malloc(sizeof(*eit_mon));
    if (eit_mon == NULL)
        return NULL;

    eit_mon->context = context;
    eit_mon->mon = tssp_dvb_eit_monitor_attach(context->parser,
                                               service_id,
                                               table_id,
                                               1,
                                               eit_monitor_callback,
                                               eit_mon);
    if (eit_mon->mon) {
        STAILQ_INSERT_TAIL(&context->eit_monitors, eit_mon, entry);
    } else {
        free(eit_mon);
        eit_mon = NULL;
    }

    return eit_mon;
}

static
void sdt_monitor_callback(struct tssp_dvb_sdt_monitor *monitor,
                          struct tssp_dvb_sdt *sdt,
                          struct tssp_section *section,
                          void *userdata)
{
    struct context *context = userdata;
    struct tssp_dvb_sdt_service *sdt_service;

    printf("### %s() called\n", __FUNCTION__);
    printf("    ts_id(0x%04x), network_id(0x%04x)\n",
           sdt->ts_id, sdt->network_id);

    STAILQ_FOREACH(sdt_service, &sdt->services, entry) {
        printf("    service_id(0x%04x), eit_schedule(%d), eit_present(%d), running_status(%d), free_ca_mode(%d)\n",
               sdt_service->service_id,
               sdt_service->eit_schedule ? 1 : 0,
               sdt_service->eit_present ? 1 : 0,
               sdt_service->running_status,
               sdt_service->free_ca_mode ? 1 : 0);

        add_eit_monitor(context,
                        sdt_service->service_id,
                        TSSP_TABLE_ID_DVB_EIT_ACTUAL_PF);
    }

    tssp_dvb_sdt_destroy(sdt);
    tssp_section_destroy(section);
}

static
void pat_monitor_callback(struct tssp_pat_monitor *monitor,
                          struct tssp_pat *pat,
                          struct tssp_section *section,
                          void *userdata)
{
    struct context *context = userdata;
    struct tssp_pat_program *program;

    printf("### %s() called\n", __FUNCTION__);
    printf("    ts_id(0x%04x)\n", pat->ts_id);

    STAILQ_FOREACH(program, &pat->programs, entry) {
        printf("    program_number(0x%04x), pid(0x%04x)\n",
               program->number, program->pid);
        if (program->number != 0)
            add_pmt_monitor(context, program->number, program->pid);
    }

    if (context->sdt_monitor)
        tssp_dvb_sdt_monitor_detach(context->sdt_monitor);
    context->sdt_monitor = tssp_dvb_sdt_monitor_attach(context->parser,
                                                       pat->ts_id,
                                                       1,
                                                       sdt_monitor_callback,
                                                       context);

    tssp_pat_destroy(pat);
    tssp_section_destroy(section);
}

int main(int argc, char *argv[])
{
    struct timespec ts[2];
    struct context context = {
        .n  = 0,
#if 1
        .tv = {0, 96 * BATCH_SIZE},
#else
        .tv = {0, 0},
#endif
        .ait_listener = NULL,
    };
#if 0
    struct tssp_section_filter filter = {
        .pid = TSSP_PID_DVB_SDT,
        .mask = {0xFF, 0},
        .value = {TSSP_TABLE_ID_DVB_SDT, 0},
        .timeout = 0,
    };
#endif

    event_init();

    STAILQ_INIT(&context.pmt_monitors);
    STAILQ_INIT(&context.ait_monitors);
    STAILQ_INIT(&context.eit_monitors);
    context.parser = tssp_create(tssp_timer_impl_create,
                                 tssp_timer_impl_delete,
                                 NULL);
#if 0
    void *listener = tssp_add_section_filter(context.parser,
                                             &filter,
                                             sdt_callback,
                                             &context);
#endif
    /* add PAT monitor */
    context.pat_monitor = tssp_pat_monitor_attach(context.parser,
                                                  1,
                                                  pat_monitor_callback,
                                                  &context);

    evtimer_set(&context.ev, packet_timer_cb, &context);
    evtimer_add(&context.ev, &context.tv);

    clock_gettime(CLOCK_MONOTONIC, &ts[0]);
#if 1
    while (event_dispatch() == 1);
#else
    printf("event_dispatch() = %d\n", event_dispatch());
#endif
    clock_gettime(CLOCK_MONOTONIC, &ts[1]);

    printf("done : %u kbps\n",
           188*8*context.n/1024/(unsigned int)(ts[1].tv_sec - ts[0].tv_sec));

    /* remove EIT listener */
    // TODO
    /* remove SDT listener */
    if (context.sdt_monitor)
        tssp_dvb_sdt_monitor_detach(context.sdt_monitor);
    /* remove PAT listener */
    tssp_pat_monitor_detach(context.pat_monitor);
#if 0
    tssp_remove_section_filter(context.parser, context.pat_listener);
#endif
    context.pat_listener = NULL;

    tssp_destroy(context.parser);

    return 0;
}

